﻿/**
 *    Copyright 2012 Yohan Courbe
 *
 *  Licensed under the Apache License, Version 2.0 (the "License 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *      
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

using System;
using System.Timers;
using NetworkAnalyzer.SQL;
using PacketDotNet;

namespace NetworkAnalyzer.Engines.Statistics
{
    public class StatisticsEngine : Engine
    {
        private StatisticsSummaryList _statisticsSummaries;
        private DateTime _startTime;
        private readonly Timer _timer = new Timer();
        private readonly Object _summaryLock = new Object();

        public StatisticsEngine()
        {
            Console.WriteLine("Statistics Engine started.");

            Name = "Statistics";

            _statisticsSummaries = new StatisticsSummaryList();
            _timer.Interval = 300000;
            _timer.AutoReset = true;
            _timer.Elapsed += FlushDatabase;
            _timer.Start();
            _startTime = DateTime.Now;
        }

        private void FlushDatabase(Object sender, ElapsedEventArgs e)
        {
            //Console.WriteLine("[{0}] Flushing StatEngine {1} entry", DateTime.Now, _statisticsSummaries.Count);
            var dbConnect = MysqlDatabaseConnection.OpenConnection();

            StatisticsSummaryList statisticsSummaries;
            
            lock (_summaryLock)
            {
                statisticsSummaries = _statisticsSummaries;
                _statisticsSummaries = new StatisticsSummaryList();
                _startTime = DateTime.Now;
            }

            dbConnect.Query("SET AUTOCOMMIT=0;");
            dbConnect.Query("START TRANSACTION;");

            foreach (StatisticsSummary statisticsSummary in statisticsSummaries)
            {
                //Console.WriteLine("\tLocalhost {0}", statisticsSummary.Localhost);
                
                dbConnect.Query(string.Format("INSERT INTO `summary`(`started_at`, `duration`, `localhost`) VALUES ('{0}','{1}',{2});",
                        _startTime.GetMySqlFormattedDate(),
                        _timer.Interval / 1000,
                        statisticsSummary.Localhost.ToInt()
                    )
                );

                // Workarround qui pue à mort.
                var ds = dbConnect.SelectQuery(string.Format("SELECT id FROM summary WHERE started_at = '{0}' and localhost = '{1}'", _startTime.GetMySqlFormattedDate(), statisticsSummary.Localhost.ToInt()));
                var lastInsertId = (int) ds.Tables[0].Rows[0][0];

                foreach (ProtocolUsage protocolUsage in statisticsSummary.ProtocolUsages)
                {
                    //Console.WriteLin---e("\t\tRemotehost {0} Port {1} Protocol {2} App {3}", protocolUsage.Remotehost, protocolUsage.Port, protocolUsage.IpProtocolType, protocolUsage.ApplicativeProtocol);
                    dbConnect.Query(string.Format("INSERT INTO `protocolusage`(`summary_id`, `remote_host`, `ip_protocol`, `port`, `applicative_protocol`, `upload_bandwidth`, `download_bandwidth`) VALUES ('{0}','{1}','{2}','{3}','{4}','{5}','{6}');", lastInsertId, protocolUsage.Remotehost.ToInt(), protocolUsage.IpProtocolType, protocolUsage.Port, protocolUsage.ApplicativeProtocol, protocolUsage.UploadBandwidth, protocolUsage.DownloadBandwidth));
                }
            }
            
            dbConnect.Query("COMMIT;");
            dbConnect.CloseConnection();

            //Console.WriteLine("[{0}] Done", DateTime.Now);
        }

        public override void Start()
        {
            
        }

        public override void Stop()
        {
           
        }

        public override void AddPacket(Packet packet)
        {
            //var eth = packet as EthernetPacket;

            //if (eth == null) return;
            //switch (eth.Type)
            //{
            //    case EthernetPacketType.IpV6:
            //    case EthernetPacketType.IpV4:
            //        {
            //            var ip = IpPacket.GetEncapsulated(packet);

            //            switch (ip.Protocol)
            //            {
            //                case IPProtocolType.TCP:
            //                    {
            //                        var tcp = TcpPacket.GetEncapsulated(packet);

            //                        if (ip.SourceAddress.IsLocalHost())
            //                        {
            //                            lock (_summaryLock)
            //                            {
            //                                _statisticsSummaries.GetStatisticsSummary(ip.SourceAddress).AddTcpPacket
            //                                    (ip, tcp, (uint) packet.Bytes.Length);
            //                            }
            //                        }
            //                        else if (ip.DestinationAddress.IsLocalHost())
            //                        {
            //                            lock (_summaryLock)
            //                            {
            //                                _statisticsSummaries.GetStatisticsSummary(ip.DestinationAddress).
            //                                    AddTcpPacket(ip, tcp, (uint) packet.Bytes.Length);
            //                            }
            //                        }
            //                    }
            //                    break;

            //                case IPProtocolType.UDP:
            //                    {
            //                        var udp = UdpPacket.GetEncapsulated(packet);

            //                        if (ip.SourceAddress.IsLocalHost())
            //                        {
            //                            lock (_summaryLock)
            //                            {
            //                                _statisticsSummaries.GetStatisticsSummary(ip.SourceAddress).AddUdpPacket
            //                                    (ip, udp, (uint) packet.Bytes.Length);
            //                            }
            //                        }
            //                        else if (ip.DestinationAddress.IsLocalHost())
            //                        {
            //                            lock (_summaryLock)
            //                            {
            //                                _statisticsSummaries.GetStatisticsSummary(ip.DestinationAddress).
            //                                    AddUdpPacket(ip, udp, (uint) packet.Bytes.Length);
            //                            }
            //                        }
            //                    }
            //                    break;
            //            }
            //        }
            //        break;
            //}
        }

        public override void AddIpTcpPacket(Packet packet, IpPacket ipPacket, TcpPacket tcpPacket)
        {
            if (Tools.IsLocalHost(ipPacket.SourceAddress))
            {
                lock (_summaryLock)
                {
                    _statisticsSummaries.GetStatisticsSummary(ipPacket.SourceAddress).AddTcpPacket
                        (ipPacket, tcpPacket, (uint)packet.Bytes.Length);
                }
            }
            else if (Tools.IsLocalHost(ipPacket.DestinationAddress))
            {
                lock (_summaryLock)
                {
                    _statisticsSummaries.GetStatisticsSummary(ipPacket.DestinationAddress).
                        AddTcpPacket(ipPacket, tcpPacket, (uint)packet.Bytes.Length);
                }
            }
        }

        public override void AddIpUdpPacket(Packet packet, IpPacket ipPacket, UdpPacket udpPacket)
        {
            if (Tools.IsLocalHost(ipPacket.SourceAddress))
            {
                lock (_summaryLock)
                {
                    _statisticsSummaries.GetStatisticsSummary(ipPacket.SourceAddress).AddUdpPacket
                        (ipPacket, udpPacket, (uint)packet.Bytes.Length);
                }
            }
            else if (Tools.IsLocalHost(ipPacket.DestinationAddress))
            {
                lock (_summaryLock)
                {
                    _statisticsSummaries.GetStatisticsSummary(ipPacket.DestinationAddress).
                        AddUdpPacket(ipPacket, udpPacket, (uint)packet.Bytes.Length);
                }
            }
        }
    }
}